﻿using System.Collections;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Parser.Objects.Collections;
using Storm.TextEditor.Parser.XML;

namespace Storm.TextEditor.Parser.Objects
{
    /// <summary>
    /// Represents a block of code in a document.
    /// </summary>
    /// <remarks>
    /// The Block class represents a specific code/text element
    /// such as a string, comment or the code itself.
    /// 
    /// A Block can contain keywords, operators, scopes and child Blocks.
    /// 
    /// 
    /// For example, if we were to describe the language C#,
    /// we would have the following blocks:
    /// 
    /// Code block						- the Block containing all the keywords and operators.
    /// Singleline comment block		- a Block that starts on // - terminates at the end of a line.
    /// Multiline comment block			- a Block that starts on /* - can span multiple rows and terminates on */.
    /// String block					- a Block that starts on "  - terminates on " or at the end of a line.
    /// Char block						- a Block that starts on '  - terminates on ' or at the end of a line.
    /// 
    /// <b>CHILD BLOCKS:</b>
    /// The main Block would have all other blocks as child blocks, since they can only appear inside the
    /// Block. A string can for example never exist inside a comment in C#.
    /// A Block can also have itself as child block.
    /// For example, the C# Block can have itself as a child block and use the scope patterns "{" and "}".
    /// This way we can accomplish FOLDING since the parser will know where a new scope starts and ends.
    /// 
    /// <b>SCOPES:</b>
    /// Scopes describe what patterns starts and what patterns end a specific Block.
    /// For example, the C# multiline comment have the scope patterns /* and */.
    /// 
    /// <b>KEYWORDS:</b>
    /// A Keyword is a pattern that can only exist between separator chars.
    /// For example the keyword "for" in C# is valid if it is contained in something like: "for (...)".
    /// 
    /// <b>OPERATORS:</b>
    /// Operators is the same thing as keywords but are valid even if there are no separator chars around them.
    /// In most cases operators are only one or two chars such as ":" or "->"
    /// Operators in this context should not be mixed up with code operators such as "and" or "xor" in VB6.
    /// In this context they are keywords.
    /// </remarks>
    public class Block
    {
        #region Fields

        private PatternListList keywordsList = null;
        private PatternListList operatorsList = null;

        private ScopeCollection scopePatterns = null;
        private BlockCollection childBlocks = new BlockCollection();

        private TextStyle style = null;
        private Color backColor = Color.Transparent;

        private string name = "";
        private bool multiline = false;
        private bool terminateChildren = false;

        private Language parent = null;
        private Hashtable lookupTable = new Hashtable();

        private PatternCollection simplePatterns = new PatternCollection();
        private PatternCollection complexPatterns = new PatternCollection();

        #endregion

        #region Properties

        /// <summary>
        /// Gets the simple patterns.
        /// </summary>
        /// <value>The simple patterns.</value>
        public PatternCollection SimplePatterns
        {
            get { return simplePatterns; }
        }

        /// <summary>
        /// Gets whether the background color of the Block is transparent.
        /// </summary>
        public bool Transparent
        {
            get { return (this.backColor.A == 0); }
        }

        /// <summary>
        /// Gets or sets the language parent of the Block.
        /// </summary>
        public Language Parent
        {
            get { return parent; }
            set { parent = value; }
        }

        /// <summary>
        /// A list of keyword groups.
        /// For example, one keyword group could be "keywords" and another could be "datatypes".
        /// These groups could have different color shemes assigned to them.
        /// </summary>
        public PatternListList KeywordsList
        {
            get { return keywordsList; }
            set { keywordsList = value; }
        }

        /// <summary>
        /// A list of operator groups.
        /// Each operator group can contain its own operator patterns and its own color shemes.
        /// </summary>
        public PatternListList OperatorsList
        {
            get { return operatorsList; }
            set { operatorsList = value; }
        }

        /// <summary>
        /// A list of scopes, most blocks only contain one scope, eg. a scope with start and end patterns for example "/*" and "*/"
        /// for multiline comments, but in some cases you will need more scopes, eg. PHP uses both "&lt;?", "?&gt;" and "&lt;?PHP", "PHP?&gt;".
        /// </summary>
        public ScopeCollection ScopePatterns
        {
            get { return scopePatterns; }
            set { scopePatterns = value; }
        }

        /// <summary>
        /// A list containing which Blocks are valid child blocks in a specific block.
        /// eg. strings and comments are child blocks for a Block.
        /// </summary>
        public BlockCollection ChildBlocks
        {
            get { return childBlocks; }
            set { childBlocks = value; }
        }

        /// <summary>
        /// The style to use when colorizing the content of a block,
        /// meaning everything in this block except keywords, operators and ChildBlocks.
        /// </summary>
        public TextStyle Style
        {
            get { return style; }
            set { style = value; }
        }

        /// <summary>
        /// The name of this block.
        /// Names are not required for blocks but can be a good help when interacting with the parser.
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        /// <summary>
        /// The background color of a block.
        /// </summary>
        public Color BackColor
        {
            get { return backColor; }
            set { backColor = value; }
        }

        /// <summary>
        /// Gets or sets whether the Block can span multiple lines or if it should terminate at the end of a line.
        /// </summary>
        public bool Multiline
        {
            get { return multiline; }
            set { multiline = value; }
        }

        /// <summary>
        /// Gets or sets if the parser should terminate any ChildBlock when it finds an end scope Pattern for this block.
        /// For example %&gt; in ASP terminates any ASP block, even if it appears inside an ASP string.
        /// </summary>
        public bool TerminateChildren
        {
            get { return terminateChildren; }
            set { terminateChildren = value; }
        }

        /// <summary>
        /// Gets or sets the lookup table of the Block.
        /// </summary>
        public Hashtable LookupTable
        {
            get { return lookupTable; }
            set { lookupTable = value; }
        }

        /// <summary>
        /// Gets or sets the collection of complex Patterns in the Block.
        /// </summary>
        public PatternCollection ComplexPatterns
        {
            get { return complexPatterns; }
            set { complexPatterns = value; }
        }

        #endregion

        #region Methods

        #region Public

        /// <summary>
        /// Resets the lookup table of the Block.
        /// </summary>
        public void ResetLookupTable()
        {
            if (lookupTable != null)
                lookupTable.Clear();

            simplePatterns.Clear();
            complexPatterns.Clear();
        }

        /// <summary>
        /// Adds a Pattern to the lookup table of the Block.
        /// </summary>
        /// <param name="pattern">The Pattern to add.</param>
        public void AddToLookupTable(Pattern pattern)
        {
            if (pattern.IsComplex == true)
                complexPatterns.Add(pattern);
            else
                simplePatterns.Add(pattern);
        }

        /// <summary>
        /// Builds the lookup table of the Block.
        /// </summary>
        public void BuildLookupTable()
        {
            PatternComparer comparer = new PatternComparer();
            simplePatterns.Sort(comparer);

            foreach (Pattern pattern in simplePatterns)
            {
                if (pattern.StringPattern.Length <= 2)
                {
                    char ch = pattern.StringPattern[0];

                    if (pattern.Parent.CaseSensitive == false)
                    {
                        char ch1 = char.ToLower(ch);
                        if (lookupTable[ch1] == null)
                            lookupTable[ch1] = new PatternCollection();

                        PatternCollection patterns = lookupTable[ch1] as PatternCollection;
                        if (patterns.Contains(pattern) == false)
                            patterns.Add(pattern);

                        char ch2 = char.ToUpper(ch);
                        if (lookupTable[ch2] == null)
                            lookupTable[ch2] = new PatternCollection();

                        patterns = lookupTable[ch2] as PatternCollection;
                        if (patterns.Contains(pattern) == false)
                            patterns.Add(pattern);
                    }
                    else
                    {
                        if (lookupTable[ch] == null)
                            lookupTable[ch] = new PatternCollection();

                        PatternCollection patterns = lookupTable[ch] as PatternCollection;
                        if (patterns.Contains(pattern) == false)
                            patterns.Add(pattern);
                    }
                }
                else
                {
                    string stringPattern = pattern.StringPattern.Substring(0, 3).ToLower();

                    if (lookupTable[stringPattern] == null)
                        lookupTable[stringPattern] = new PatternCollection();

                    PatternCollection patterns = lookupTable[stringPattern] as PatternCollection;
                    if (patterns.Contains(pattern) == false)
                        patterns.Add(pattern);
                }
            }
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of Block.
        /// </summary>
        public Block()
        {
            keywordsList = new PatternListList(this);
            operatorsList = new PatternListList(this);

            style = new TextStyle();

            keywordsList.Parent = this;
            keywordsList.IsKeyword = true;

            operatorsList.Parent = this;
            operatorsList.IsOperator = true;
            scopePatterns = new ScopeCollection(this);
        }

        /// <summary>
        /// Initializes a new instance of Block.
        /// </summary>
        /// <param name="parent">Language parent of the Block.</param>
        public Block(Language parent)
            : this()
        {
            this.Parent = parent;
            this.Parent.ChangeVersion();
        }
    }
}
